%% ---
%%  Excerpted from "Programming Erlang",
%%  published by The Pragmatic Bookshelf.
%%  Copyrights apply to this code. It may not be used to create training material, 
%%  courses, books, articles, and the like. Contact us if you are in doubt.
%%  We make no guarantees that this code is fit for any purpose. 
%%  Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
%%---

-module(chat_group).
-import(lib_chan_mm, [send/2, controller/2]).
-import(lists, [foreach/2, reverse/2]).

-export([start_group/6]).

start_group(ServerMM, Parent, Group, Nick, Address, Port) ->
	process_flag(trap_exit, true),
	controller(ServerMM, self()),
	register(list_to_atom(Group), self()),
	lib_chan:start_server(Nick++".conf"),
	io:format("Grupo ~p criado em ~p:~p~n", [Group, Address, Port]),
	send(ServerMM, {ack, Group, Address, Port}),
	group_controller(ServerMM,Parent,Group,[]).


delete(Pid, [{Pid,Nick}|T], L) -> {Nick, reverse(T, L)};
delete(Pid, [H|T], L)          -> delete(Pid, T, [H|L]);
delete(_, [], L)               -> {"????", L}.



group_controller(ServerMM,Parent,Group,L) ->
    receive
	{mm, C, {relay, Nick, Str}} ->
	    foreach(fun({Pid,_}) -> send(Pid, {msg,Nick,C,Str}) end, L),
	    group_controller(ServerMM,Parent,Group,L);
	{mm, C, {login, Nick}} ->
	    send(C, ack),
	    self() ! {mm, C, {relay, Nick, "I'm joining the group"}},
	    %% Ex 1
	    self() ! {update_buddies},
	    %% Ex 1
	    group_controller(ServerMM,Parent,Group,[{C,Nick}|L]);
	{mm_closed, C} ->
	    {Nick, L1} = delete(C, L, []),
	    self() ! {mm, C, {relay, Nick, "I'm leaving the group"}},
	    self() ! {update_buddies},
	    group_controller(ServerMM,Parent,Group,L1);
	%% Ex 1
	{update_buddies} ->
		foreach(fun({Pid,_}) -> send(Pid, {buddies, L}) end, L),
		group_controller(ServerMM,Parent,Group,L);
	%% Ex 1
	%% Ex 2
	{mm, C, {get_nicks_in_group, InspectedGroup}} ->
	    send(ServerMM, {get_nicks_in_group, InspectedGroup}),
	    wait_get_nicks_in_group_response(C, L),
	    group_controller(ServerMM,Parent,Group,L);
	{chan, From, get_nicks} ->
	    send(From, {nicks, L}),
	    group_controller(ServerMM,Parent,Group,L);
	%% Ex 2
	%% Ex 3
	{mm, C, get_all_groups} ->
	    AllGroups = lib_chan:rpc(ServerMM, get_all_groups),
	    send(C, AllGroups),
        group_controller(ServerMM,Parent,Group,L);
    %% Ex 3
    %% Ex 4
    {mm, C, {send_pvt, Nick, To, Str}} ->
	    case lookup(To, L) of
		{ok, Pid} -> 
		    send(Pid, {pvt_msg, Nick, C, Str}),
		    send(C, {pvt_msg, Nick, C, Str});  % Ecoa mensagem para a origem
		error ->
		    io:format("Erro: Destino desconhecido")
	    end,
        group_controller(ServerMM,Parent,Group,L);
    %% Ex 4
    {'EXIT', Parent, _Reason} ->
		foreach(fun({Pid,_}) -> send(Pid, group_died) end, L), % envia morte do grupo para todos os membros
		send(ServerMM, {groupDied,Group});
	{'EXIT', C, _Reason} ->
		self() ! {mm_closed, C},
		group_controller(ServerMM,Parent,Group,L);
	Any ->
	    io:format("group controller received Msg=~p~n", [Any]),
	    group_controller(ServerMM,Parent,Group,L)
    end.

%% Ex 2
wait_get_nicks_in_group_response(C, L) ->
    receive
	    {chan, _Channel, {nicks_in_group, Nicks}} ->
	        send(C, {nicks_in_group, Nicks});
	    % chat_server pode fazer uma consulta a esse grupo
	    % enquanto ele espera a resposta, portanto eh necessario
	    % saber responder get_nicks aqui
	    {chan, Channel, get_nicks} ->
            send(Channel, {nicks, L}),
            wait_get_nicks_in_group_response(C, L);
        Other ->
	        io:format("chat_group unexpected message waiting nicks in group Msg=~p~n", [Other]),
            wait_get_nicks_in_group_response(C, L)
	end.
%% Ex 2

%% Ex 4
lookup(N, [{Pid,N}|_]) -> {ok, Pid};
lookup(N, [_|T])       -> lookup(N, T);
lookup(_,[])           -> error.
%% Ex 4
